package cn.anc.httpcontrolutil;

import android.annotation.TargetApi;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Build;
import android.util.Log;

import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.Socket;

import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;

/**
 * Created by baobaoding on 2017/12/24.
 */

public class SocketUtils {
    private static String TAG = SocketUtils.class.getSimpleName();
    public static Network  wifiNetwork;



    public static void setWifiNetwork(Network network) {
        wifiNetwork = network;
    }


    private static ConnectivityManager connectivityManager;
    private static void switchWIFINetwork() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && SocketUtils.wifiNetwork != null) {
            ConnectivityManager.setProcessDefaultNetwork(SocketUtils.wifiNetwork);
        }
    }
    @TargetApi(Build.VERSION_CODES.M)
    public static void forceSendRequestByWifiData(Context context) {
        switchWIFINetwork();
        connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            try {
            NetworkRequest.Builder builder = new NetworkRequest.Builder();
            builder.addCapability(NET_CAPABILITY_INTERNET);
            builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
            NetworkRequest networkRequest = builder.build();
            ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    super.onAvailable(network);
                    SocketUtils.setWifiNetwork(network);
                    if (Build.VERSION.SDK_INT >= 23) {
                        connectivityManager.bindProcessToNetwork(network);
                    } else {
                        // 23后这个方法舍弃了
                        ConnectivityManager.setProcessDefaultNetwork(network);
                    }
                }

                @Override
                public void onLost(Network network) {
                    super.onLost(network);
                    SocketUtils.setWifiNetwork(null);
                    connectivityManager.unregisterNetworkCallback(this);

                }
            };
            connectivityManager.requestNetwork(networkRequest, networkCallback);
            } catch (Exception e) {
            }
        }
    }


    private static int bindSocketToNetwork(int socketfd, Network network) {
        if (network == null) {
            return -1;
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return -1;
        }
        Log.d(TAG, "start bindSocketToNetwork");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            FileDescriptor fileDescriptor = new FileDescriptor();
            try {
                Field field = FileDescriptor.class.getDeclaredField("descriptor");
                field.setAccessible(true);
                field.setInt(fileDescriptor, socketfd);
                network.bindSocket(fileDescriptor);
                Log.d(TAG, "bindSocketToNetwork success: network" + network + "+socketfd" + socketfd);
                return 0;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return -1;
    }

    private static int bindSocketToNetwork(Socket socket, Network network) {
        if (network == null) {
            return -1;
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return -1;
        }
        try {
            network.bindSocket(socket);
            return 0;
        } catch (Exception ex) {
            Log.e(TAG, "--------------------------------failed to bind Socket to Net error is :" + ex.getMessage());
        }
        return -1;
    }


    public static int bindSocketToWifiNetwork(int socketfd) {
        return bindSocketToNetwork(socketfd, wifiNetwork);
    }



    public static int bindSocketToWifiNetwork(Socket socket) {
        return bindSocketToNetwork(socket, wifiNetwork);
    }

    /*
    public static int getNetworkId(Network paramNetwork) throws Exception
    {
        Field localField = Class.forName("android.net.Network").getDeclaredField("netId");
        localField.setAccessible(true);
        return localField.getInt(paramNetwork);
    }*/

    public static void closeResources(Closeable... resources) {
        for (Closeable resource : resources) {
            try {
                resource.close();
            } catch (IOException e) {
                // Ignore
            }
        }
    }
}
